using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using RS.DOM.Construction;
using RS.DOM.Types;

namespace RS.DOM.Common
{
    #region Enumerations

    public enum TokenType
    {
        None,
        Area,
        Bracket,
        Number,
        Collection,
        Collections,
        Float,
        String,
        Item,
        Operator,
        Name,
        MutableName,
        Bool,
        Len,
        New,
        Strategy,
        Me,
        Mutable,
        Null,
        SubExpression,
        Rational
    }

    public enum OperationType
    {
        None,
        Plus,           //+
        Multiply,       //*
        Subtract,       //-
        Devide,         // /
        DevideWithRest, //%
        Agregate,       //\
        Generate,       //..
        Apply,          //=
        DoubleApply,    //===
        AddApply,       //,=
        PlusApply,      //+=
        Dimension,      //D
        Great,          //>
        Less,           //<
        LessEqual,      //>=
        Equal,          //==
        Add,            //,
        Compare,        //?
        CompareInNot,   //?:
        NotNull,        //:
        Next,           //;
        And,            //&
        Or,             //|
        Point,          //.
        Rational,       //./
        Power            //^
    }

    #endregion

    public class Token
    {
        #region Constructors

        public Token(Context context)
        {
            m_tokenType = TokenType.None;
            m_operationType = OperationType.None;
            m_context = context;
            m_squareBracketPriority = 1;
            m_bracketPriority = 1;
            m_priority = 1;
            m_text = string.Empty;
        }

        public Token(Context context, object result)
        {
            m_tokenType = TokenType.None;
            m_operationType = OperationType.None;
            m_context = context;
            m_squareBracketPriority = 1;
            m_bracketPriority = 1;
            m_priority = 1;
            m_text = string.Empty;
            m_result = result;
        }

        public Token(Context context, string text, bool isMultiply)
        {
            m_tokenType = TokenType.None;
            m_operationType = OperationType.None;
            m_context = context;
            m_squareBracketPriority = 1;
            m_bracketPriority = 1;
            m_priority = 1;
            m_text = text;
            m_isMultiply = isMultiply;
        }

        #endregion

        #region Members

        private string m_text;
        private Token m_prevToken;
        private Token m_nextToken;
        private bool m_isMultiply;
        private int m_squareBracketPriority;
        private int m_bracketPriority;
        private int m_priority;
        private TokenType m_tokenType;
        private object m_result;
        private OperationType m_operationType;
        private Context m_context;
        private bool m_isResolved;
        

        #endregion

        #region Properties

        public Token PrevToken
        {
            get { return m_prevToken; }
            set { m_prevToken = value; }
        }

        public Token NextToken
        {
            get { return m_nextToken; }
            set { m_nextToken = value; }
        }

        public object Result
        {
            get { return m_result; }
            set { m_result = value; }
        }

        public TokenType TokenType
        {
            get { return m_tokenType; }
            set { m_tokenType = value; }
        }
        
        public bool IsMultiply
        {
            get { return m_isMultiply; }
            set { m_isMultiply = value; }
        }

        public string Text
        {
            get { return m_text; }
            set { m_text = value; }
        }

        public int SquareBracketPriority
        {
            get
            {
                return m_squareBracketPriority;
            }

            set
            {
                m_squareBracketPriority = value;
            }
        }

        public int BracketPriority
        {
            get
            {
                return m_bracketPriority;
            }

            set
            {
                m_bracketPriority = value;
            }
        }

        public int Priority
        {
            get
            {
                return m_priority;
            }

            set
            {
                m_priority = value;
            }
        }

        public OperationType OperationType
        {
            get
            {
                return m_operationType;
            }

            set
            {
                m_operationType = value;
            }
        }

        public bool IsResolved
        {
            get
            {
                return m_isResolved;
            }

            set
            {
                m_isResolved = true;
            }
        }

        public Context Context
        {
            get
            {
                return m_context;
            }

            set
            {
                m_context = value;
            }
        }

        #endregion

        #region Methods

        public Token Build()
        {
            Token token = this;

            int res1;
            float res2;
            bool res3;

            if (this.Text[0]=='\'')
            {
                token = new Types.String(this.Context, this.Text.Replace("'", "")); //fix it later
            }
            else if (this.Text[0] == '!')
            {
                token = new MutableName(this.Context, this.Text.Substring(1));
            }
            else if (int.TryParse(this.Text, out res1))
            {
                token = new Number(this.Context, res1);
            }
            else if (float.TryParse(this.Text, out res2))
            {
                token = new Float(this.Context, res2);
            }
            else if (bool.TryParse(this.Text, out res3))
            {
                token = new Bool(this.Context, res3);
            }
            else if (this.Text == "i" ||
                     this.Text == "j")
            {
                token = new Mutable(this.Context, this.Text);
            }
            else if (this.Text == "$")
            {
                token = new Area(this.Context, this.Context.Document.Area);
            }
            else if (this.Text == "new")
            {
                token = new New(this.Context);
            }
            else if (this.Text == "strategy")
            {
                token = new Strategy(this.Context);
            }
            else if (this.Text == "null")
            {
                token = new Null(this.Context);
            }
            else if (this.Text == "(" ||
                     this.Text == ")" ||
                     this.Text == "[" ||
                     this.Text == "]")
            {
                this.TokenType = TokenType.Bracket;
                this.Result = null;
            }
            else if (this.Text == "+")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Plus;
            }
            else if (this.Text == "-")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Subtract;
            }
            else if (this.Text == "*")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Multiply;
            }
            else if (this.Text == "/")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Devide;

            }
            else if (this.Text == "..")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Generate;

            }
            else if (this.Text == "\\")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Agregate;

            }
            else if (this.Text == "./")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Rational;
            }
            else if (this.Text == "**")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Dimension;

            }
            else if (this.Text == "=")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Apply;

            }
            else if (this.Text == "||")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.DoubleApply;

            }
            else if (this.Text == "==")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Equal;

            }
            else if (this.Text == "<")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Less;

            }
            else if (this.Text == "<=")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.LessEqual;

            }
            else if (this.Text == ">")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Great;

            }
            else if (this.Text == ".")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Point;

            }
            else if (this.Text == ",")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Add;

            }
            else if (this.Text == "%")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.DevideWithRest;

            }
            else if (this.Text == "?")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Compare;

            }
            else if (this.Text == "?:")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.CompareInNot;

            }
            else if (this.Text == ":")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.NotNull;
            }
            else if (this.Text == ";")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Next;
            }
            else if (this.Text == "&")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.And;
            }
            else if (this.Text == "|")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Or;
            }
            else if (this.Text == ",=")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.AddApply;
            }
            else if (this.Text == "+=")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.PlusApply;
            }
            else if (this.Text == "^")
            {
                this.TokenType = TokenType.Operator;
                this.OperationType = OperationType.Power;
            }
            else
            {
                token = new Name(this.Context, this.Text);
            }

            return token;
        }

        public virtual Token Resolve()
        {
            m_isResolved = true;
            return this;
        }

        public Token DoUnarOperation(OperationType type)
        {
            Token result = null;

            if (this.TokenType == TokenType.Null)
            {
                result = new Null(this.Context);
            }
            else
            {
                result = this.UnarOperation(type);
            }

            return result;
        }

        public Token DoOperation(Token token, OperationType type)
        {
            Token result = null;

            if (type == OperationType.Next)
            {
                result = token;
            }
            else if (type == OperationType.NotNull)
            {
                if (this.TokenType != TokenType.Null)
                    result = this;
                else
                    result = token;
            }
            else if (this.TokenType == TokenType.Null || token.TokenType == TokenType.Null)
            {
                result = new Null(this.Context);
            }
            else
            {
                result = this.Operation(token, type);
            }

            return result;
        }

        public virtual Token Operation(Token token, OperationType type)
        {
            throw new Exception("'" + type.ToString() + "' operation for the " + this.GetType().ToString() + " doesn't support.");
        }

        public virtual Token UnarOperation(OperationType type)
        {
            return this; //throw new Exception("'" + type.ToString() + "' unar operation for the " + this.GetType().ToString() + " doesn't support.");
        }

        public virtual Token Create()
        {
            return this;
        }

        public Token Copy()
        {
            Token token = this.Create();
            token.BracketPriority = this.BracketPriority;
            token.SquareBracketPriority = this.SquareBracketPriority;
            token.Text = this.Text;
            token.TokenType = this.TokenType;
            token.OperationType = this.OperationType;
            token.Priority = this.Priority;

            return token;    
        }
        
        public override string ToString()
        {
            if (m_text != string.Empty)
                return m_text;
            else if (m_result != null)
                return m_result.ToString();
            else
                return string.Empty;
        }

        #endregion
    }
}
